package de.fmannan.addbook.domainmodel;

import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IAdaptable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import de.fmannan.addbook.menu.actions.SaveAddressBookAction;
import de.fmannan.addbook.persistence.xmlwriter.XMLWriter;
import de.fmannan.addbook.views.ContactTableView;
import de.fmannan.addbook.views.ContactTreeView;

/**
 * @author fmannan
 * 
 * All existing groups are registered here. This class has to be a singleton.
 */
public class GroupRegistrar implements IContact, IAdaptable {

	private static final Logger log = Logger.getLogger(GroupRegistrar.class);
	private static GroupRegistrar instance;
	private static Hashtable<String, Group> registrar;
	
	private GroupRegistrar() {
		registrar = new Hashtable<String, Group>();
	}

	/**
	 * Retrieves an instance of the singleton class GroupRegistrar.
	 * 
	 * @return an instance of {@link GroupRegistrar}
	 */
	public static GroupRegistrar getInstance() {
		if(instance == null) {
			instance = new GroupRegistrar();
		}
		return instance;
	}
	
	/**
	 * Registers a new group and makes it available for adding contacts to it.
	 * 
	 * @param group
	 *            the group which should be registered
	 */
	public void addChild(Group group) {
		log.debug("Registering new Group with GroupRegistrar");
		registrar.put(group.getGroupName(),group);
		group.setParent(this);
		group.setNewlyCreated(false);
		ContactTreeView.getInstance().refresh();
	}
	
	/**
	 * Removes a registered group (delete). All contacts currently in this group
	 * will be shifted to the default group.
	 */
	public void removeChild(Group group) {
		log.debug("Unregistering group from GroupRegistrar");
		registrar.remove(group.getGroupName());
		group.setParent(null);
		ContactTreeView.getInstance().refresh();
		if (ContactTableView.getInstance() != null)
			ContactTableView.getInstance().refresh();				
	}

	@Override
	public Object[] getChildren() {
		Set <String> set = registrar.keySet();
		Group [] array = new Group [registrar.size()];
		int counter = 0;
		for(String s : set) {
			array[counter] = registrar.get(s);
			counter++;
		}
		return array;
	}

	@Override
	public Object getParent() {
		return null;
	}
	
	/**
	 * @return a specific Group with the given Name
	 */
	public Group getGroup(String groupName) {
		return registrar.get(groupName);
	}
	
	public String [] getGroupNames() {
		Set <String> set = registrar.keySet();
		String [] array = new String [registrar.size()];
		int counter = 0;
		for(String s : set) {
			array[counter] = s;
			counter++;
		}		
		return array;
	}

	@Override
	public boolean hasChildren() {
		return !registrar.isEmpty();
	}

	/**
	 * Called on plugin startup to initialize the tree model from a save-File.
	 * @throws ParserConfigurationException 
	 * @throws IOException 
	 * @throws SAXException 
	 */
	public void startup() throws ParserConfigurationException, SAXException, IOException {
		log.debug("Loading contact-data from file");
		GroupRegistrar.getInstance();
		
		// Creating DOM Reader
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(new File(SaveAddressBookAction.getPathToSaveFile()));	
		Element root = doc.getDocumentElement();
		
		// Delegate parsing the child nodes to the appropriate classes
		NodeList groups = root.getElementsByTagName("group");
		log.debug("Name of root-node is: " + root.getNodeName() + " Number of groups is: " + groups.getLength());
		for(int i = 0; i < groups.getLength(); i++) {
			instance.addChild(Group.buildFromXML(groups.item(i)));
		}
	}
	
	@Override
	public Object getAdapter(Class adapter) {
		return null;
	}

	@Override
	public void setParent(IContact parent) {
		// Has no parent
	}

	/**
	 * Creates a new Group and returns it to the calling method.
	 * 
	 * @return a newly created Group
	 */
	public Group createGroup() {
		log.debug("Creating new group");
		Group group = new Group("");
		group.setNewlyCreated(true);
		return null;
	}
	
	@Override
	public void writeToXML(XMLWriter out) throws IOException {
		log.debug("Writing GroupRegistrar to XML");

		// First we write the encoding into the file
		out.writeText("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
		// Write the root node (open it)
		out.writeStartElement("registrar");
		
		// Iterate over all children and write these elements to the file
		for(Object o : getChildren()) {
			// Write the name of the Group in the opening tag
			out.writeStartElement("group");
			
			// Delegate the remaining XML-building to the Group so that it may
			// fill in its own attributes and children
			((Group)o).writeToXML(out);
			
			// Close the Group tag
			out.writeEndElement();			
		}
		
		// Close the root node
		out.writeEndElement();
		out.flush();
		out.close();
		out = null;
	}
}
